The Raspberry, Yocto Project and The TPM


I'm updating the article constantly to make it more readable and easy to reproduce.

I Updated the content so it fits with the Raspberry Pi 4

We can now use the TPM2 SLB9670 with U-Boot and the repo is up to date :

Last Update : 04/13/2020


In the cybersecurity field we need to play with crypto primitives. It allows us to authenticate for services (ssh, vpn ...), encrypt files for confidentiality, sign mail for proving your identity to the recipient, and even securing the boot of a complex device ...

So you do need to store keys and use crypto algorithms such as RSA, ECDH, AES compliant with some criteria (industry, military, medical ...).

In this article we will create our own distro using the Yocto Project for a RaspberryPi4 coupled with a TPM2.0 that I just purchased few months ago. Also we will patch U-Boot in order to make it communicates with the TPM2.


Notice that since the First Stage Boot Loader is closed source, we won't be able to create a real Secure Boot.


If you have any question or suggestion, you can contact me at pierre[dot]ftn[at]pfontaine[dot]fr. Also, here is a repo with some sources

By abuse of language I will write "Yocto" instead of "Yocto Project".


To follow this article, it will be easier if you have some knowledge on how U-Boot works, the concept behind Device Tree, the concept behind modules, and last but not least if you have already been playing with Yocto.

Small Yocto Distro

A TPM module can natively be used with a Linux Kernel greater than 4.14.85 according to the datasheet. Hence we have to select the right branch of Poky when cloning the project using git. We have to search which branch of the meta-raspberrypi is based on a kernel greater than the 4.14.85.

Here is a link describing the release activity for the Yocto Project : And everything should be right for development that started after the Warrior release.

Adding Extra Layers

We want our distro to support our RaspberryPi4, so we need to clone the Board Support Package named meta-raspberrypi. This BSP Meta depends on the meta-openembedded, so we have to clone this one too.

git clone -b zeus git://
git clone -b zeus

Let's clone also the meta-security layer and we will be done with cloning !

git clone -b zeus git://

Also, we create our own layer to bring some Kernel support and add some features.

bitbake-layers create-layer meta-slb9670-rpi

Then, we integrate those different layers using the bitbake-layers command.


For the next commands I move into the build_rpi directory.

bitbake-layers add-layer ../meta-openembedded/meta-oe
bitbake-layers add-layer ../meta-openembedded/meta-python
bitbake-layers add-layer ../meta-openembedded/meta-multimedia
bitbake-layers add-layer ../meta-openembedded/meta-networking
bitbake-layers add-layer ../meta-raspberrypi
bitbake-layers add-layer ../meta-security/meta-tpm
bitbake-layers add-layer ../meta-slb9670-rpi

Edit the Configuration File

In the build_rpi directory, you will find the conf/local.conf file. Edit this file with your favorite editor and add the following lines :

MACHINE ??= "raspberrypi4"
RPI_EXTRA_CONFIG = "dtoverlay=letstrust-tpm"

KERNEL_CLASSES = "kernel-fitimage"
KERNEL_FITCONFIG = "conf@bcm2711-rpi-4-b.dtb"

MACHINE_FEATURES_append = "tpm2"

DISTRO_FEATURES_append = " systemd tpm2 "
VIRTUAL-RUNTIME_init_manager = "systemd"
VIRTUAL-RUNTIME_initscripts = ""

Here, we told bitbake that:

  • we want to build the project for the RaspberryPi4 using a 32 bits Kernel (Using 64 bits is a bit problematic the one who to use fitImage for now)
  • we want to enable the UART, actually I'm using an USB/UART cable plugged on the GPIO (gnd, Rx, Tx).
  • we want to significantly improve our build process by using all available ressources, you should change this line if you don't have 6 physical cores. (hint : lscpu)
  • we want to use the Das U-Boot bootloader
  • we want to enable the SPI Bus, since the TPM Module communicates using this kind of bus.
  • we want to add our overlay named "letstrust-tpm", it will be explained later but in order to tell our Kernel that the TPM Module is an external Module we'll need to provide a Device Tree Source.
  • we want to add the tpm2 feature.

Back on Our Own Layer

Remember that we have created a layer named meta-slb9670-rpi.

The first thing to do is to create a recipes-core/images/ file, and add the following lines :

include recipes-core/images/

DESCRIPTION = "A Core image based on core-image-base for rpi"

IMAGE_FEATURES_append = " ssh-server-dropbear "

IMAGE_INSTALL_append = " tpm2-tools libtss2 libtss2-tcti-device libtss2-tcti-mssim tpm2-abrmd tpm2-pkcs11 slb9670"

KERNEL_DEVICETREE =+ "overlays/letstrust-tpm.dtbo"


include recipes-core/images/ will create an image without any modules, however we do need them ! So base this image on core-image-base.

Here we also include some stuff from the meta-tpm allowing us to communicate with the module. At the end of IMAGE_INSTALL_append, we added slb9670, this recipes will be discuted in a next section.

Then, we need to modify the kernel to add some modules and the Device Tree source. A folder named recipes-kernel/linux is required. The last line with KERNEL_DEVICETREE statement depend on the next part, it allows to add the compiled Device Tree to the SD Image else it would be compiled but left in our deploy/image/raspberrypi4 folder.

Add Device Tree Source

Previously, in the configuration file located at poky/build_rpi/conf/local.conf we added a line "dtoverlay=letstrust-tpm". Our first step is to find the Device Tree Source working with this module. Here is the link where you can find the file :

This Device Tree is a key element in our project. Since there is no Hardware SPI driver developped in the U-Boot project for the Raspberry SoC, we decided to make a workaround by using the soft gpio driver and then control the SPI pins by using the bit bang strategy.

So move to the Poky directory to execute the following command. It will take the file that we have just downloaded and create specific .bbappend in meta-slb9670-rpi/recipes-kernel/linux/.

recipetool appendsrcfile -wm raspberrypi4 meta-slb9670-rpi/ virtual/kernel ./letstrust-tpm-overlay.dts 'arch/${ARCH}/boot/dts/overlays/letstrust-tpm-overlay.dts'

You can observe that the execution of this command create a file in recipes-kernel-linux named linux-raspberrypi_%.bbappend and a folder named linux-raspberrypi.

Inside linux-raspberrypi_%.bbappend you may have :


SRC_URI += "file://9670.cfg \
            file://letstrust-tpm-overlay.dts;subdir=git/arch/${ARCH}/boot/dts/overlays \


And we will add just one line :

KERNEL_DEVICETREE += "overlays/letstrust-tpm.dtbo"

You can notice that this line is the same than the one added in our

Add Kernel Configurations

I based my configuration on this script :

Move to meta-slb9670-rpi/recipes-kernel/linux/ and create a folder named files. Then in this folder create a file named 9670.cfg and add those lines :


With those configurations we can choose what we want to be used as module (m), what we want to be compiled in the kernel (y) and what should not be used (n).

Now, we need to tell bitbake that those configurations have to be used when compiling the kernel for our RaspberryPi3 machine. Stay in meta-slb9670-rpi/recipes-kernel/linux/ and create a file named linux-raspberrypi_4.%.bbappend. I added 2 lines for this file :

FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
SRC_URI += "file://9670.cfg"


The name of the file is important ! The name of the kernel used for the RaspberryPi is linux-raspberrypi and the "%" is a wildcard that say "It's ok for every minors of the 4th version".

Create a Service for Enabling TPM at Boot Time

For instant we would have to manually instantiate with modprobe the TPM module to start using it.

modprobe tpm_tis_spi

However, here is a workaround which implies to create a new recipe. I choose to name this one recipes-services under meta-slb9670-rpi for adding some stuff.

mkdir recipes-services
cd recipes-services

In this folder let's create a folder named files which will contain our slb9670-rpi.service.

mkdir files
touch files/slb9670-rpi.service

In this .service file add those following lines :

Description=Service Systemd launching TPM communication via modprobe

ExecStart=modprobe tpm_tis_spi


Go back to the recipes-services folder and create the script. Its purpose will be to copy our .service in the right folder.

SUMMARY = "Install a service that instantiate the communication between the raspberry and the TPM SLB9670"

LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "file://slb9670-rpi.service"

do_install() {
    install -d ${D}${systemd_system_unitdir}
    install -d ${D}${sysconfdir}/systemd/system/
    install -m 0644 ${WORKDIR}/slb9670-rpi.service ${D}${systemd_system_unitdir}
    ln -s ${D}${systemd_system_unitdir}/slb9670-rpi.service ${D}${sysconfdir}/systemd/system/

FILES_${PN} += "${systemd_system_unitdir}/slb9670-rpi.service"


The script above is quite basic, I added a small workaround with the symbolic link. I don't want the user to run systemctl [start|enable] slb9670-rpi, so it basically do the thing done by the systemctl enable command.

Later, when you will boot your system for the first time you will see that the services has started in the middle of the others services :

[  OK  ] Started Serial Getty on ttyS0.
[  OK  ] Reached target Login Prompts.
[  OK  ] Started Service Systemd la…PM communication via modprobe.
[    8.995529] Bluetooth: Core ver 2.22
[    9.000853] NET: Registered protocol family 31
[    9.005534] Bluetooth: HCI device and connection manager initialized
[    9.012311] Bluetooth: HCI socket layer initialized
[    9.016614] tpm_tis_spi spi0.1: 2.0 TPM (device-id 0x1B, rev-id 22)

Patching U-Boot

Fix the SPI communication

In a first time we were not able to communicate with the TPM2 using the TPM commands exposed by U-Boot. Although there is a hard SPI driver for Linux, the U-Boot one is missing. However, the GPIO driver is present and thus we can talk to our TPM using bit-banging technic through the soft-spi driver.

A recent work done by J. Holland allows us to patch the U-Boot, he found that some values supposed to enable the Clock Polarity and the Clock Phase were hardcoded. This patch was applied to a recent commit, and since some changes were made between this recent commit (April 2020) and the commit used in the Yocto Project for the Zeus release (v2019.07) I had to do some handiwork.

First I pulled the U-Boot project from its git repo and then I applied the first patch (dm-spi-fix-CPHA-and-implement-CPOL-for-soft-spi.diff) given by J. Holland in its git repo

Then back to my Yocto build directory I used the devtool modify -x command against u-boot to modify it and create a new patch. It fetch the u-boot 2019.07 in a dedicated folder and apply patches present in others meta.

devtool modify -x u-boot ./u-boot-devtool

So now, we have a new directory named u-boot-devtool. Move inside, create a branch for making some changes.

cd u-boot-devtool/
git branch patch_spi_tpm
git checkout patch_spi_tpm

Then I made a copy past from the recent u-boot repo patched for the both files : drivers/spi/soft_spi.c and drivers/tpm/tpm2_tis_spi.c. Then back in u-boot-devtool I substituted the content of those files.

When you make changes, you can build the image to test if it works, once it works we create a patch by commiting and using the git format-patch command.

git add -A :/
git commit -s
# It opens your favourite editor, here you write a nice commit message
git format-patch master --stdout > 001-fix-soft-spi.patch

Then, copy past this patch under the reciped-bsp/u-boot/u-boot/ and update the bitbake script :

SRC_URI += "file://fragment.cfg;subdir=git \
            file://001-fix-soft-spi.patch \

Once you are done with the u-boot-devtool repo, you can lauch the following commands :

devtool reset u-boot
rm -rf u-boot-devtool/

Fix the fitImage configuration

Also, by default using the fitImage it will load the bcm2708-rpi-zero-w.dtb and in our case we want to use the bcm2711-rpi-4-b.dtb. So we will overload the recipe rpi-u-boot-scr present in the meta-raspberrypi.

devtool modify rpi-u-boot-scr

Then we can edit the imported file in workspace/sources/rpi-u-boot-scr/.

fdt addr ${fdt_addr} && fdt get value bootargs /chosen bootargs
fatload mmc 0:1 ${kernel_addr_r} @@KERNEL_IMAGETYPE@@
@@KERNEL_BOOTCMD@@ ${kernel_addr_r}#@@KERNEL_FITCONFIG@@ - ${fdt_addr}
devtool update-recipe -a ../meta-slb9670-rpi/ rpi-u-boot-scr

Then we edit the .bbappend file present in meta-slb9670-rpi/recipes-bsp/rpi-u-boot-scr and we add the following content :

do_compile() {
        -e 's/@@KERNEL_BOOTCMD@@/${KERNEL_BOOTCMD}/' \
        "${WORKDIR}/" > "${WORKDIR}/boot.cmd"
    mkimage -A arm -T script -C none -n "Boot script" -d "${WORKDIR}/boot.cmd" boot.scr

It will overwrite the do_compile present in the meta-raspberrypi.

Compile Time

Now that everything is set, let's move to the build_rpi folder and execute :

bitbake core-image-slb9670-rpi

Once bitbake has done its work you can move to build_rpi/tmp/deploy/images/raspberrypi3 and execute your favorite command to copy the core-image-slb9670-rpi-raspberrypi3.rpi-sdimg on your formated SD card !

dd if=core-image-slb9670-rpi-raspberrypi4.rpi-sdimg of=/dev/mmcblk0 bs=4M status=progress conv=fsync

Check the Contents of the Image

Now you can mount the SD Card on your computer, and check the raspberrypi partition which contains configuration files, and .dbt files and the overlays folder. So we can check that the overlay is added to the config.txt file.

[pfontaine@precision raspberrypi]$ cat config.txt | grep dtoverlay

And we can check if the letstrust-tpm.dtbo is present in the overlays folder.

[pfontaine@precision raspberrypi]$ ls overlays/ | grep letstrust-tpm.dtbo

Put the SD Card in the RaspberryPi and Start the System

Although I use the UART USB cable, HDMI cable + screen will do the job.

alternate text

The placement of the TPM Module is explained in the datasheet.

So insert the SD Card in the slot, open a terminal and a Serial tool like picocom :

sudo picocom -b 115200 -l /dev/ttyUSB0

Then power on the Raspberry and you will see the boot sequence which consist of U-Boot lines, followed by Systemd lines and then a prompt asking your login. Since we are in debug mode, we can use the root user without password.

At this step if we check for available devices in /dev we won't be able to see our /dev/tpm0.

Test the TPM

U-Boot command

If previously you let the Kernel launch itself, you can interrupt the process by hitting any key.

Then you can test the communication with the TPM2 with the following commands :

U-Boot> tpm2 init
U-Boot> tpm2 startup TPM2_SU_CLEAR
U-Boot> tpm2 get_capability 0x6 0x106 0x200 2
Capabilities read from TPM:
Property 0x00000106: 0x534c4239
Property 0x00000107: 0x36373000

The value obtained are read from the memory of the TPM2, and it's just SLB9670 written in hexadecimal.

python3 -c "print(bytes.fromhex('534c423936373000'))"

Analysing the Communication

How do we prove that, when playing with tpm2 commands we do talk to the TPM ? We know that we are facing a module that can communicate through SPI and hopefully I've got a logic analyzer ! On the image below you will find how I connected my BitScope's probes between RaspberryPi and the TPM.

alternate text

Then I launched the BitScope Logic application on my computer and setted it to perform a capture when I launch one of the command supposed to communicate with the TPM.

tpm2_readpublic -c key.ctx --format pem -o key-pub.pem
alternate text

For now it only show that that there is a communication between our two devices. Packet decoded are not shown here because I'm still having errors on this side.

Said that, we can also print properties of the TPM2 :

root@raspberrypi4:~# tpm2_getcap -c properties-fixed
WARN: More data to be queried: capability: 0x6, property: 0x100

  as UINT32:                0x08322e3000
  as string:                "2.0"
TPM_PT_LEVEL:               0
TPM_PT_REVISION:            1.38
TPM_PT_DAY_OF_YEAR:         0x00000008
TPM_PT_YEAR:                0x000007e2
TPM_PT_MANUFACTURER:        0x49465800
  as UINT32:                0x534c4239
  as string:                "SLB9"
  as UINT32:                0x36373000
  as string:                "670"
  as UINT32:                0x00000000
  as string:                ""
  as UINT32:                0x00000000
  as string:                ""

And looking at the vendor strings, we do clearly talk to the SLB9670.

A Simple Example

Now that our distro support the TPM, we can use utilities from the meta-tpm that we have added.

For more information about the command, and when using the zeus branch, you should refer to the documentation for the version 3.x.x.

root@raspberrypi3:~# tpm2
tpm2-abrmd               tpm2_hash                tpm2_pcrlist
tpm2_activatecredential  tpm2_hmac                tpm2_quote
tpm2_certify             tpm2_listpersistent      tpm2_rc_decode
tpm2_create              tpm2_load                tpm2_readpublic
tpm2_createpolicy        tpm2_loadexternal        tpm2_rsadecrypt
tpm2_createprimary       tpm2_makecredential      tpm2_rsaencrypt
tpm2_dictionarylockout   tpm2_nvdefine            tpm2_send
tpm2_encryptdecrypt      tpm2_nvlist              tpm2_sign
tpm2_evictcontrol        tpm2_nvread              tpm2_startup
tpm2_getcap              tpm2_nvreadlock          tpm2_takeownership
tpm2_getmanufec          tpm2_nvrelease           tpm2_unseal
tpm2_getpubak            tpm2_nvwrite             tpm2_verifysignature
tpm2_getpubek            tpm2_pcrevent
tpm2_getrandom           tpm2_pcrextend

So, as a hello world test we can for example compute the digest of the .ash_history file which contains the last command you have done.

root@raspberrypi3:~# tpm2_hash .ash_history
root@raspberrypi3:~# ls
root@raspberrypi3:~# tpm2_hash .ash_history

More Complex Example

The following example is inspired from the presentation made by Philip Tricca for the Linux Security Summit : "Getting Started with the TPM2 Software Stack (TSS2) - Philip Tricca, Intel".

Create a context file using the command below, you can specify :

  • the target hierarchy (o, p, e or n) with the option "-H"
  • the hash algorithm for generating the object name with the option "-g"
  • the algorithm used for generating the primary key with the option "-G"
  • the output file for storing the context with the option "-C"
tpm2_startup -c
tpm2_takeownership -c
tpm2_createprimary -H o -g sha256 -G rsa -C primary.ctx

> ObjectAttribute: 0x00030072
> CreatePrimary Succeed ! Handle: 0x800000ff



  • o : TPM_RH_OWNER
    Also known as Storage Hierarchy, this one is intended to be used by the end user. Data persist through reboot.
    The platform hierarchy is intended to be under the control of the platform manufacturer. Data persist through reboot.
    The Endorsement hierarchy should be used for high sensitive value, and is intended for the end user. Flag, policy and authorization value are independant from the other hierarchies. Data persist through reboot.
  • n : TPM_RH_NULL
    The Null hierarchy can be used for storing primary key. However value does not persist through boot, hence it mostly use for sessions or as a crypto co processor.

For more information see the book "A Practical Guide to TPM2.0" Chapter 9.

The tpm2_createprimary command has created a file primary.ctx

> primary.ctx

With the version 3.0.X we have the choice between those algorithms :

For this example, I suggest to use the default one which is RSA, this way we can create a pair of public and private key :

tpm2_create -c primary.ctx -g sha256 -Grsa -u -r key.priv
> algorithm:
     value: sha256
     raw: 0xb
     value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt|sign
     raw: 0x60072
     value: rsa
     raw: 0x1
     rsa: a4f10e2a5c4ef1f48333875cb8fc7ff32c03e0a5593085fc88ef06520f43c974a88bc58a08af414e90090725716403003a516a4171be8a350618aa6e6cf157e068de57bea0536a2f9f5da2916b1835fb7cf47a4672bc3384030b072bde294e18bea1bd2b91f1924855437c133fada8bf762f4055ae4a7f1f272e11961940d34f1669634229fac565114f8420bf0cf72c47ea99c8abbbb82011e9cce96558cad979b45d525b7a515960a5625cbab5a4175db7b5124955c31e4f9c2e846610b0587c448c827570ba3c2365d1cd4d5e8552842bbbe053bf337e00b3e9db7d1201397aa7d88 22bcb944286f58bbda5d8654cda3779b28a9ca24e48cfa4a218bd879

Then, once the key are correctly generated, we want them to be loaded into the TPM module so we can start using them. To do so, we use the tpm2_load command as shown below and we attach a context file.

tpm2_load -c primary.ctx -u -r key.priv -C key.ctx

Now, the next step would be to share our public key. Hence, we will create a PEM file that can be understood by OpenSSL.

tpm2_readpublic -c key.ctx --format pem -o key-pub.pem

> name: 000b3e0356d7b3f72d6ffa699ab337899a6a44bd5246116d99f4196c7634ff8b3cea
  qualified name: 000b70edf26adc3ce18438b0baef47405a8c9ed0d1c049e5bc83d166297f191e3e6e
    value: sha256
    raw: 0xb

We have generated the key-pub.pem file, we can check that the output is in the right format with the cat command.

cat key-pub.pem
> -----BEGIN PUBLIC KEY-----
  -----END PUBLIC KEY-----

I copied the public key on my laptop and created a very sensitive message, then I cipher the message with the RSA public key and send it back to the RaspberryPi using the SCP command.

touch sensitive-file.txt
echo "I'm a very sensitive message !" > sensitive-file.txt
openssl rsautl -encrypt -inkey pub.pem -pubin -in sensitive-file.txt -out sensitive-file.txt.enc
scp sensitive-file.txt.enc root@

Back to the RaspberryPi, we check that we received our encrypted file previously send via scp. We ask the TPM Module to decipher the encrypted file using the private key stored in the TPM.

> key-pub.der             key.priv                sensitive-file.txt.enc
  key.ctx                 primary.ctx

tpm2_rsadecrypt -c key.ctx -I sensitive-file.txt.enc -o sensitive-file.txt

cat sensitive-file.txt
> I'm a very sensitive message !

To Conclude

I have learned so much doing this experimentation and hope it will help you if you are trying to implement this kind of solution ! I would like to thanks the Yocto Community through the help brougth via the mailing list, the author of, the author of the meta-raspberrypi and all the contributor to the stuff used in this small project.


Hardware Tour


The RaspberryPi is one of the most used development board. It can be used for embedded prototyping or even for making an office desktop.

alternate text

Well it has an HDMI port, a 64 bit CPU, a Micro SD slot, 40-pin extended GPIO for a decent price so it's quite a good choice for beginners and students. If you want more information on the specifications, you will find them here :

LetsTrust TPM Module

This TPM Module is based on the Infineon OPTIGA SLB 9670 TPM2.0 chip. This one is certified with Common Criteria EAL4+ and FIPS 140-2.

alternate text

This device will use 10 pins of your extended GPIO. Actually it communicates through the SPI protocol.

I bought it on the Reichelt website :

For the Yocto beginners

Clone Poky zeus + Docker

Our first step will be to clone the Poky project where we want to build everything.


Make sure that you have enough space, since a build can consume up to 50Gb.

git clone -b zeus git://

Once this step is done we can directly set up our container.

cd poky
docker run --rm -it -v $(pwd):$(pwd) crops/poky --workdir=$(pwd)

Bitbake and Build

For this step we will source the oe-init-build-env script. It will create a folder with the name passed as first argument, set environment variables ...

source oe-init-build-env build_rpi

Now, we have a build_rpi directory and we can now add some third party layers.